package db

import (
	"context"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/bson/primitive"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
	"log"
	"reflect"
	"strings"
	"time"
)

const dbPrefix = "go"

type MongoDbConn struct {
	*mongo.Client
	*mongo.Database
	*options.ReplaceOptions
	*options.UpdateOptions
	context.Context
}

func NewMongoDbConn(uri string, dbName string) *MongoDbConn {

	ctx, _ := context.WithTimeout(context.Background(), 30*time.Second)
	client, err := mongo.Connect(ctx, options.Client().ApplyURI(uri))
	if err != nil {
		panic(err)
	}
	return &MongoDbConn{
		Client:         client,
		Database:       client.Database(dbName),
		ReplaceOptions: options.Replace().SetUpsert(true),
		UpdateOptions:  options.Update().SetUpsert(true),
	}
}

func (mongoDbConn MongoDbConn) Save(saveObj BaseSaveObj) bool {

	t := reflect.TypeOf(saveObj)
	tName := t.Kind().String()
	if tName != "ptr" {
		log.Println("saveObj must be pointer[Save]")
		return false
	}
	cName := strings.Split(t.String(), ".")
	collectionName := cName[len(cName)-1]
	coll := mongoDbConn.Database.Collection(strings.ToLower(dbPrefix + "_" + collectionName))
	ctx, _ := context.WithTimeout(context.Background(), 30*time.Second)
	if saveObj.GetObjId() == primitive.NilObjectID {
		reflect.ValueOf(saveObj).Elem().FieldByName("OId").Set(reflect.ValueOf(primitive.NewObjectID()))
		_, err := coll.InsertOne(ctx, saveObj)
		if err != nil {
			log.Println("mongodb insert err")
			return false
		}
		return true
	} else {
		r, err := coll.ReplaceOne(ctx, bson.M{"_id": saveObj.GetObjId()}, saveObj, mongoDbConn.ReplaceOptions)
		if err != nil {
			log.Println("mongodb replace err")
			return false
		}
		return r.UpsertedCount == 1
	}
	return false
}

// FindOne
func (mongoDbConn MongoDbConn) FindOne(filter interface{}, saveObj BaseSaveObj) bool {
	//tName := t.Kind().String()
	//if tName != "ptr" {
	//	log.Println("saveObj must be pointer![FindOne]")
	//	return nil
	//}
	t := reflect.TypeOf(saveObj)
	cName := strings.Split(t.String(), ".")
	collectionName := cName[len(cName)-1]
	coll := mongoDbConn.Database.Collection(strings.ToLower(dbPrefix + "_" + collectionName))
	ctx, _ := context.WithTimeout(context.Background(), 30*time.Second)
	r := coll.FindOne(ctx, filter)
	_, success := ctx.Deadline()
	if !success {
		log.Println("mongodb FindOne timeout!")
		return false
	}
	err2 := r.Decode(saveObj)
	if err2 != nil {
		log.Println("mongodb FindOne err!")
		return false
	}
	return true
}

func (mongoDbConn MongoDbConn) HasObj(filter interface{}, saveObj BaseSaveObj) (bool, error) {
	t := reflect.TypeOf(saveObj)
	cName := strings.Split(t.String(), ".")
	collectionName := cName[len(cName)-1]
	coll := mongoDbConn.Database.Collection(strings.ToLower(dbPrefix + "_" + collectionName))
	ctx, _ := context.WithTimeout(context.Background(), 30*time.Second)
	count, err := coll.CountDocuments(ctx, filter)
	return count > 0, err
}
